import * as Mongoose from "mongoose";
import { EventEmitter } from "events";
import * as path from "path";
import * as glob from 'glob';
import * as https from 'https';
import { common } from './middleware/common';

/**
 * 登录用户信息
 */
export interface ILoginner {
    id: String;
    name: String;
}
// /**
//  * 登录用户信息
//*/
export class loginer implements ILoginner {
    id: String;
    name: String;
    structure: String;
    area: any;
    position: String;
}

export class MongooseFactory {
    private static SchameOptions: Mongoose.SchemaOptions = {
        versionKey: false,
        timestamps: { createdAt: 'createTime', updatedAt: 'updateTime' }
    }
    private static ChildSchameOptions: Mongoose.SchemaOptions = {
        versionKey: false,
        id: false,
        _id: false
    };

    private static SchemaDefinition: Mongoose.SchemaDefinition = {
        isdel: { type: Boolean, default: false, required: true },
        creator: { type: Mongoose.Schema.Types.Mixed },
        editor: { type: Mongoose.Schema.Types.Mixed }
    };

    static Schema(definition: Mongoose.SchemaDefinition, options?: Mongoose.SchemaOptions) {
        let finalopts = this.SchameOptions as any;
        if (options) {
            var _options = options as any;
            for (var key in _options) {
                if (_options.hasOwnProperty(key)) {
                    var element = _options[key];
                    if (element) {
                        finalopts[element]
                    }
                }
            }
        }
        var _defDefinition = this.SchemaDefinition as any;
        for (var key in _defDefinition) {
            if (_defDefinition.hasOwnProperty(key)) {
                var element = _defDefinition[key];
                definition[element] = _defDefinition[key];
            }
        }
        definition.isdel = {
            type: Boolean,
            default: false
        };
        return new Mongoose.Schema(definition, finalopts) as any;
    }

    static ChildSchema(definition: Mongoose.SchemaDefinition, options?: Mongoose.SchemaOptions) {
        let finalopts = this.ChildSchameOptions as any;
        if (options) {
            var _options = options as any;
            for (var key in _options) {
                if (_options.hasOwnProperty(key)) {
                    var element = _options[key];
                    if (element) {
                        finalopts[element]
                    }
                }
            }
        }
        return new Mongoose.Schema(definition, finalopts);
    }

    static Model(name: string, schema: Mongoose.Schema) {
        const model = Mongoose.model<Mongoose.Document>(name, schema);
        model.on("error", (err: Error) => {
            mongooseModelError.error(err);
        });
        return model;
    }
}


class QueryOptions {
    filter?: Object;
    sort?: Object;
    populateOptions?: Mongoose.ModelPopulateOptions | Array<Mongoose.ModelPopulateOptions>;
    select?: String | Object;

    static get defaultSort() {
        return { createTime: -1 };
    }
}

export class ServiceBase {
    constructor(model: Mongoose.Model<Mongoose.Document>, filter?: any) {
        this.Model = model;
        if (filter) {
            this.defaultFilter = Object.assign({}, this.defaultFilter, filter);
        };
    }
    private defaultFilter: any = {};
    protected Model: Mongoose.Model<Mongoose.Document>;

    /**
     * 过滤条件生成器
     * 过滤条件上添加 逻辑删除的过滤条件
     * @param filter 过滤条件
     */
    protected filterBuilder(filter?: any) {
        let andFilter: Array<any> = [];

        filter = Object.assign({}, this.defaultFilter, filter || {});
        if (filter.isdel) {
        } else {
            let isDelFilter = [{ isdel: false }, { isdel: { '$exists': false } }];
            if (filter["$or"]) {
                andFilter = [{ "$or": isDelFilter }, { "$or": filter["$or"] }];
                delete filter["$or"];
            } else {
                filter["$or"] = isDelFilter;
            }
        }

        //TODO:合并and
        if (filter["$and"]) {
            filter["$and"] = andFilter.concat(filter["$and"]);
        } else {
            if (andFilter && andFilter.length > 0) {
                filter["$and"] = andFilter;
            }
        }

        return filter;
    }

    /**
     * Mongoose.DocumentQuery查询对象生成器
     * @param options 过滤条件
     * @param single 是否是查询单个对象
     */
    private queryBuildAuto(options: QueryOptions, single: Boolean) {
        // options 
        {
            options.filter = this.filterBuilder(options.filter);

            if (!options.sort) {
                options.sort = QueryOptions.defaultSort;
            }
        }
        // end options

        const querydoc = single ? this.Model.findOne(options.filter) : this.Model.find(options.filter);

        if (options.sort) {
            querydoc.sort(options.sort);
        }

        if (options.select) {
            querydoc.select(options.select);
        }
        if (options.populateOptions) {
            querydoc.populate(options.populateOptions);
        }
        return querydoc;
    }

    /**
     * Mongoose.DocumentQuery查询对象生成器 多个
     * @param options 过滤条件
     */
    private queryBuildMulti(options: QueryOptions) {
        return this.queryBuildAuto(options, false) as Mongoose.DocumentQuery<Mongoose.Document[], Mongoose.Document>;
    }

    /**
     * Mongoose.DocumentQuery查询对象生成器 单个
     * @param options 过滤条件
     */
    private queryBuildSingle(options: QueryOptions) {
        return this.queryBuildAuto(options, true) as Mongoose.DocumentQuery<Mongoose.Document, Mongoose.Document>;
    }
    /**
     * 分页查询
     * @param limit 每页显示数量
     * @param offset 偏移多少条 如每页显示10条  第一页偏移0  第二页 偏移10 第三页偏移20
     * @param options 查询过滤条件
     */
    protected async queryPagination(offset: number, limit: number, options?: QueryOptions) {
        options = options || {};
        var query = this.queryBuildMulti(options);
        query.limit(limit);
        query.skip((offset - 1) * limit);

        const count = await this.count(options.filter, false);
        const rows = await query.then((res) => res.map((value) => value.toJSON() as any));
        return {
            rows: rows,
            total: count,
            offset: offset,
            limit: limit,
            totalpage: Math.ceil(count / limit)
        };
    }

    /**
     * 
     * @param options 查询所有
     */
    protected async queryAll(options?: QueryOptions): Promise<Array<any>> {
        options = options || {};
        let query = this.queryBuildMulti(options);
        return await query.then((res) => res.map((value) => value.toJSON()));

    }

    /**
     * 查询单挑记录
     * @param filter 查询条件
     * @param options 
     */
    async querySingle(filter: Object, options?: QueryOptions): Promise<any> {
        options = options || {};
        options.filter = filter;
        const model = await this.queryBuildSingle(options)
        if (model) {
            return model.toJSON();
        } else {
            return null;
        }
    }

    /**
     * 根据id查询
     * @param id id
     */
    async queryById(id: String): Promise<any> {
        return  this.querySingle({ _id: id });
    }
    /**
     * 新增
     * @param doc 对象
     * @param user 操作用户 
     */
    async create(doc: any, user?: ILoginner): Promise<any> {
        doc.code = common.buildCode();
        doc.updateTime = Date.now();
        doc.createTime = Date.now();
        var model = await new this.Model(doc).save();
        //TODO:保存失败的异常
        return model.toJSON();
    }
    /**
     * 新增多条
     * @param docs 多个对象
     * @param user 
     */
    async createMany(docs: Array<Object>, user?: ILoginner): Promise<Array<any>> {
        var model = await this.Model.insertMany(docs);
        //TODO:保存失败的异常
        return model.map(item => { return item.toJSON(); });
    }
    /**
     * 更新
     * @param id 
     * @param doc 
     * @param user 
     */
    async update(id: String, doc: any, user?: ILoginner): Promise<any> {
        doc.updateTime = Date.now();
        var model = await this.Model.findOneAndUpdate({ _id: id }, doc, { new: true });
        return model.toJSON();
    }
    /**
     * 启用
     * @param id 
     */
    async open(id: String, user?: ILoginner): Promise<any> {
        var model = await this.Model.findOneAndUpdate({ _id: id }, { status: 1, updateTime: Date.now() }, { new: true });
        return model.toJSON();
    }
    /**
     * 禁用
     * @param id 
     */
    async lock(id: String, user?: ILoginner): Promise<any> {
        var model = await this.Model.findOneAndUpdate({ _id: id }, { status: 0, updateTime: Date.now() }, { new: true });
        return model.toJSON();
    }

    /**
     * 更新多条
     * @param id 
     * @param doc 
     * @param user 
     */
    async updateMany(filter: Object, doc: any, user?: ILoginner) {
        doc.updateTime = Date.now();
        await this.Model.update(filter, doc, { multi: true });
    }

    /**
     * 删除
     * 删除操作使用的逻辑删除 并非真正意义上的删除
     * @param id 
     * @param user 
     */
    async delete(id: String, user?: ILoginner) {
        return await this.update(id, { isdel: true }, user);
    }
    /**
     * 按条件删除
     * 删除操作使用的逻辑删除 并非真正意义上的删除
     * @param id 
     * @param user 
     */
    async deleteofwhere(filter: Object, user?: ILoginner) {
        await this.updateMany(filter, { isdel: true }, user);
    }
    /**
     * 物理删除
     * @param id 
     * @param user 
     */
    async physicsdelete(id: String, user: ILoginner) {
        var model = await this.Model.findByIdAndRemove(id);
        return model.toJSON();
    }

    /**
     * 按条件物理删除
     * @param id 
     * @param user 
     */
    async physicsdeleteofwhere(filter: Object, user?: ILoginner) {
        var model = await this.Model.remove(filter);
    }

    /**
     * 得到统计数量
     * @param filter 
     * @param options 
     */
    async count(filter?: Object, convert: boolean = true) {
        filter = filter || {};
        return await this.Model.count(convert ? this.filterBuilder(filter) : filter)
    }
    /**
     * 判断是否存在
     * @param filter 
     * @param options 
     */
    async exist(filter: Object) {
        return (await this.count(filter)) > 0;
    }
    /**
     * 获取去重
     * @param field 
     */
    async distinct(field: string) {
        return await this.Model.distinct(field);
    }
    /** 判断是否为空 */
    public isNotNull(pars: any) {
        return pars && pars != '' && pars != undefined && pars != 'undefined' && pars.trim().length > 0;
    }
}



export class Tools {
    static dateToString(date: Date,
        options?: {
            y?: boolean,
            M?: boolean,
            d?: boolean,
            hms?: number,
            gap?: String
        }) {
        var config = { y: true, M: true, d: true, hms: 0, gap: '-' };
        if (options) {
            config = Tools.extend(config, options as any);
        }

        // for (var key in Object.keys(options)) {
        //     config[key] = (options as any)[key]
        // }

        var rstr = "";
        var gap = config.gap;
        if (config.y) {
            rstr += date.getFullYear();
        }
        if (config.M) {
            rstr += gap;
            let m = date.getMonth() + 1;
            if (m < 10) {
                rstr += "0";
            }
            rstr += m;
        }
        if (config.d) {
            rstr += gap;
            let d = date.getDate();
            if (d < 10) {
                rstr += "0";
            }
            rstr += d;
        }
        if (config.hms) {
            var _l = config.hms % 3;
            _l = _l === 0 ? 3 : _l;
            if (_l > 0) {
                rstr += " ";
                rstr += date.getHours()
            }
            if (_l > 1) {
                rstr += ":";
                rstr += date.getMinutes()
            }
            if (_l > 2) {
                rstr += ":";
                rstr += date.getSeconds()
            }
        }

        return rstr;
    }
    /**
     * 获取传入日期是当前第几周
     * @param date 
     */
    static dateMonthWeek(date: Date) {
        var w = date.getDay(),
            d = date.getDate();
        return Math.ceil((d + 6 - w) / 7);
    }

    static extend(obj: Object, ...arr: Object[]) {
        var r = obj as any;

        for (let i in arr) {
            let element = arr[i] as any;
            let pns = Object.getOwnPropertyNames(element);
            for (let key of pns) {
                let val = element[key]
                r[key] = val;
            }
        }
        return r;
    }
    /** 经纬度转换-度分秒转换成为度 */
    static DuFenMiaoToDegree = (value: any) => {
        if (value.indexOf("°") > -1) {
            var du = value.split("°")[0];
            var fen = value.split("°")[1].split("′")[0];
            var miao = value.split("°")[1].split("′")[1].split('″')[0];

            return Math.abs(du) + (Math.abs(fen) / 60 + Math.abs(miao) / 3600);
        } else {
            return value;
        }
    }

    static postform(url: any, data: any, fn: any) {
        data = data || {};
        var content = require('querystring').stringify(data);
        var parse_u = require('url').parse(url, true);
        var isHttp = parse_u.protocol == 'http:';
        var options = {
            host: parse_u.hostname,
            path: parse_u.path,
            method: 'POST',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'Content-Length': content.length
            }
        };
        return new Promise(function (resolve, reject) {
            var req = https.request(options, function (res: any) {
                var _data = '';
                var buffer: any = [];
                var size = 0;
                res.on('data', function (chunk: any) {
                    buffer.push(chunk);
                    size += chunk.length;
                    _data += chunk;
                });
                res.on('end', function () {
                    let result = Buffer.concat(buffer, size).toString('utf-8');
                    resolve(result);
                    fn != undefined && fn(_data);
                });
            });

            req.write(content);
            req.end();
        });
    }

    //格式化日期：yyyy-MM-dd
    static formatDate(date: any) {
        var myyear = date.getFullYear();
        var mymonth = date.getMonth() + 1;
        var myweekday = date.getDate();
        if (mymonth < 10) {
            mymonth = "0" + mymonth;
        }
        if (myweekday < 10) {
            myweekday = "0" + myweekday;
        }
        return (myyear + "-" + mymonth + "-" + myweekday);
    }

    //获得本周的开始日期
    static getWeekStartDate() {
        const now = new Date(); //当前日期
        const nowDayOfWeek = now.getDay(); //今天本周的第几天
        const nowDay = now.getDate(); //当前日
        const nowMonth = now.getMonth(); //当前月
        var nowYear = now.getFullYear(); //当前年
        nowYear += (nowYear < 2000) ? 1900 : 0;
        var weekStartDate = new Date(nowYear, nowMonth, nowDay - nowDayOfWeek);

        return this.formatDate(weekStartDate);
    }



    //获得本周的结束日期
    static getWeekEndDate() {
        const now = new Date(); //当前日期
        const nowDayOfWeek = now.getDay(); //今天本周的第几天
        const nowDay = now.getDate(); //当前日
        const nowMonth = now.getMonth(); //当前月
        var nowYear = now.getFullYear(); //当前年
        nowYear += (nowYear < 2000) ? 1900 : 0;
        var weekEndDate = new Date(nowYear, nowMonth, nowDay + (6 - nowDayOfWeek));
        return this.formatDate(weekEndDate);
    }

    //获得某月的天数
    static getMonthDays(myMonth: any, nowYear: any) {
        var monthStartDate: any = new Date(nowYear, myMonth, 1);
        var monthEndDate: any = new Date(nowYear, myMonth + 1, 1);
        var days = (monthEndDate - monthStartDate) / (1000 * 60 * 60 * 24);
        return days;
    }
    static filenamevalidation(filename: any, types: any) {
        types = types.split(',');
        var pattern = '\.(';
        for (var i = 0; i < types.length; i++) {
            if (0 != i) {
                pattern += '|';
            }
            pattern += types[i].trim();
        }
        pattern += ')$';
        return new RegExp(pattern, 'i').test(filename);
    }
}


/** 异常 */
export namespace Exceptions {
    enum Code {
        /**需要重新登录 */
        needLogin = 20000,
        tokenValidFail,

        known = 40000,
        noFound,

        DBManipulationError = 40030,
        DBStop,

        /**没有访问权限 */
        unAccessRights = 50040,

        update = 50090,
        notoken = 60000,
    };
    const kalendError = (msg: string, code: Code, original?: Error): Error => {
        let error: any = new Error(msg);
        let kalendObj: any = {};
        if (original) {
            kalendObj.original = original;
        }
        if (code) {
            kalendObj.code = code;
        }
        error["kalend"] = kalendObj;
        return error;
    };
    /** 已知错误 */
    export const unknown = (err: any) => {
        return kalendError(err.errmsg, err.errcode);
    };
    /** 已知错误 */
    export const known = (msg: string) => {
        return kalendError(msg, Code.known);
    };
    /**没有找到 */
    export const noFound = () => {
        return kalendError("不存在或已删除", Code.noFound);
    };
    /**token验证失败 */
    export function tokenValidFail() {
        return kalendError('Token验证失败', Code.tokenValidFail);
    };

    /**未获取token */
    export function notoken() {
        return kalendError('未获取到token', Code.notoken);
    };

    /**
     * 未登录
     */
    export function notLogedIn() {
        return kalendError("未登录或登录超期", Code.needLogin);
    }
    /**
     * session 请求过期
     */
    export function sessionExpired() {
        return kalendError("请求过期，请重新登录", Code.needLogin);
    }
    /** 无访问权限 */
    export function unAccessRights() {
        return kalendError("无访问权限", Code.unAccessRights);
    }

    export function mongooseModel(err: Error) {
        return kalendError('数据库操纵出错', Code.DBManipulationError, err);
    };
    /**
     * redis出错
     * @param err 
     */
    export function redisServiceStop(err: Error) {
        return kalendError('缓存服务停止', Code.DBStop, err);
    };
    /**
     * 系统更新
     */
    export function update() {
        return kalendError("系统已更新，请关闭系统后重新登录", Code.update);
    };
}

/**
 * EventEmitter 工厂
 */
class EventEmitterFactory {
    /**
     * 
     */
    private eventEmiter: EventEmitter;
    private event: symbol;

    /**
     * 
     * @param eventName event 名称
     */
    constructor(curerr: (err: Error) => any, eventName: string = 'error') {
        this.eventEmiter = new EventEmitter();
        this.event = Symbol(eventName);
        this.curerr = curerr;
    }
    curerr: (err: Error) => any;

    /**
     * 记录错误信息
     * @param err 异常信息
     */
    error(err: Error) {
        this.eventEmiter.once(this.event, () => {
            throw this.curerr(err);
        });
    }
    /**
     * 接收错误信息
     */
    emit(canThrow: boolean = true) {
        const cnt = this.eventEmiter.listenerCount(this.event);
        for (var index = 1; index <= cnt; index++) {
            if (canThrow && index === cnt) {
                this.eventEmiter.emit(this.event);
            } else {
                try {
                    this.eventEmiter.emit(this.event);
                } catch (error) {
                    console.error(error);
                }
            }
        }
    }
}
/**
 * 
 */
export const mongooseModelError = new EventEmitterFactory(Exceptions.mongooseModel);




export namespace KLENUM {


    /**
     * 注册所有的枚举类
     */
    export function register() {
        const dir = path.join(__dirname, 'enums/*.js');
        const items = glob.sync(dir);
        items.forEach((item) => require(item));
    }

    export function declaration() {
        return (target: any, name: string, descriptor: PropertyDescriptor) => {
            let STORAGE: Map<string, any> = target.constructor.__STORAGE;
            if (STORAGE) {
            } else {
                STORAGE = new Map<string, any>()
                target.constructor.__STORAGE = STORAGE;
            }

            if (STORAGE) {
                STORAGE.set(name, { value: target[name] });
            }
        };
    };

    interface ienumbasic { }
    export class enumbasic implements ienumbasic {
        private get __STORAGE(): Map<string, any> { return (this.constructor as any).__STORAGE; }
        getDictionary() {
            const lst = new Array<{ text: string, value: string }>();
            this.__STORAGE.forEach((value: { value: string }, key: string) => {
                lst.push({
                    text: value.value,
                    value: key
                });
            });
            return lst;
        };
        [key: string]: any;
        /** 转换 */
        getValue(item: string) {
            return this[item];
        };

        /** 转换多个 */
        getValues(items: Array<string>) {
            return items.map(itm => {
                return this[itm];
            });
        };
        getPropertyByValue(val: string) {
            let prop: string;
            const item = this.__STORAGE.forEach((itm, key) => {
                if (itm.value === val) {
                    prop = key;
                };
            });
            return prop;
        }

        /** 是否存在 */
        exists() {
        };
    }

}


import * as Koa from 'koa';
export namespace Kalends {
    export interface IControllerBasic {
        loginer: any;
    }
    // export class ControllerBasic implements IControllerBasic {
    //     ctx: Koa.Context;
    //     get loginer() {
    //         if (this.session) {
    //             return this.session[KLSYMBOL.loginer];
    //         }
    //         return null;
    //     };
    //     set loginer(data: any) {
    //         this.session[KLSYMBOL.loginer] = data;
    //     };

    //     private get session() {
    //         return this.ctx.session;
    //     }
    // }
};
export class RouterAttributes {
    private collection: Array<any>
    private main: any;
    constructor() {
        this.collection = [];
    };
    add(func: Function) {
        this.collection.splice(0, 0, func);
    };
    router(func: Function) {
        this.main = func
    };
    getCollection() {
        let result: Array<any> = [];
        if (this.collection.length > 0) {
            result = Object.assign([], this.collection)
        } else {
        }
        result.push(this.main);
        return result;
    };
    private static init(target: any, name: string) {
        let attrs: { [key: string]: RouterAttributes } = target[KLSYMBOL.attributes];
        if (attrs) {
        } else {
            attrs = {};
            target[KLSYMBOL.attributes] = attrs;
        }
        if (attrs[name]) {
        } else {
            attrs[name] = new RouterAttributes();
        }
        return attrs[name];
    }
    static add(target: any, name: string, func: Function) {
        this.init(target, name).add(func);
    }
    static router(target: any, name: string, func: Function) {
        this.init(target, name).router(async (ctx: Koa.Context) => {
            // target.ctx = ctx;
            /** 从ctx.querystring重置ctx.query参数对象,防止特殊字符被转换，如：+ 转为空格*/
            target.ctx = this.ctxquery(ctx);
            await func.call(target, ctx);
        });
    }
    private static ctxquery(ctx: any) {
        let queryData: any = {};
        if (ctx.querystring && ctx.querystring.length > 0) {
            let queryStrList = ctx.querystring.split('&');
            // 利用了ES6提供的forOf，可以找找相关的看看
            for (let [index, queryStr] of queryStrList.entries()) {
                // 进行切割
                let itemList = queryStr.split('=');
                queryData[itemList[0]] = decodeURIComponent(itemList[1]);
            }
        }
        ctx.query = queryData;
        return ctx;
    }
};

export const KLSYMBOL = {
    /** 所有的attributes */
    attributes: Symbol('attributes'),
    /** 路由前缀 */
    routePrefix: Symbol('routePrefix'),
    /**
     * 登录人信息
     */
    loginer: 'loginer'
};